function _do_assert(cond: boolean, failstr?: string) {
    if (! cond)
        throw new Error(failstr || 'Assertion failed.')
}

export function assert(condition: boolean, failstr?: string): asserts condition {
    _do_assert(condition, failstr)
}

export function assertDefined<T>(val: T, failstr?: string): asserts val is Exclude<T, undefined> {
    _do_assert(val !== undefined, failstr)
}

export function assertNotNull<T>(val: T, failstr?: string): asserts val is NonNullable<T> {
    _do_assert(val != null, failstr)
}

export function assertNotEmpty<T>(val: T[], failstr?: string) {
    _do_assert(val.length > 0, failstr)
}

export function assertNumber(val: unknown, failstr?: string): asserts val is number {
    _do_assert(typeof val === 'number', failstr)
}

export function assertString(val: unknown, failstr?: string): asserts val is string {
    _do_assert(typeof val === 'string', failstr)
}

export function assertBoolean(val: unknown, failstr?: string): asserts val is boolean {
    _do_assert(typeof val === 'boolean', failstr)
}

export function assertArray(val: unknown, failstr?: string): asserts val is unknown[] {
    _do_assert(Array.isArray(val), failstr)
}

export function assertFunction(val: unknown, failstr?: string): asserts val is Function {
    _do_assert(typeof val === 'function', failstr)
}

export function assertObject(val: unknown, failstr?: string): asserts val is Record<string, unknown> {
    _do_assert(Object.prototype.toString.call(val) === '[object Object]', failstr);
}

export function assertInstanceOf(val: unknown, cls: (new(...args: any) => any), failstr?: string): asserts val is InstanceType<typeof cls> {
    _do_assert(val instanceof cls, failstr)
}
